package bancosys.tec.persist.hibernate;

import java.io.Serializable;
import java.sql.Connection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import jmine.tec.persist.PersistMessages;
import jmine.tec.utils.debug.impl.DebugRuntimeFactory;

import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.Filter;
import org.hibernate.FlushMode;
import org.hibernate.Hibernate;
import org.hibernate.Interceptor;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.ReplicationMode;
import org.hibernate.SQLQuery;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.engine.EntityKey;
import org.hibernate.engine.PersistenceContext;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
import org.hibernate.event.EventListeners;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.jdbc.Batcher;
import org.hibernate.jdbc.JDBCContext;
import org.hibernate.jdbc.Work;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.stat.SessionStatistics;
import org.hibernate.type.Type;

import bancosys.tec.persist.DirtyAware;
import bancosys.tec.persist.exception.PersistenceException;
import bancosys.tec.persist.hibernate.impl.annotations.SessionTracker;
import bancosys.tec.persist.persister.AbstractPersister;
import bancosys.tec.persist.persister.DefaultPersister;

/**
 * Decorator que adiciona o tratamento de "dirty"ao objetos que implementem a interface <code>DirtyAware</code>.
 * 
 * @author Cesar Olmos
 */
@SuppressWarnings("unchecked")
public class DirtyAwareSession implements Session, SessionImplementor {

    private static final String FLUSH_IDENTIFIER = "flush";

    private final Session delegate;

    /**
     * Construtor.
     * 
     * @param delegate a session para a qual serão delegadas as chamadas.
     */
    public DirtyAwareSession(Session delegate) {
        this.delegate = delegate;
        SessionTracker.getInstance().registerOpenSession(this);
    }

    /**
     * {@inheritDoc}
     */
    public Transaction beginTransaction() {
        return this.delegate.beginTransaction();
    }

    /**
     * {@inheritDoc}
     */
    public void cancelQuery() {
        this.delegate.cancelQuery();
    }

    /**
     * {@inheritDoc}
     */
    public void clear() {
        this.delegate.clear();
    }

    /**
     * {@inheritDoc}
     */
    public Connection close() {
        SessionTracker.getInstance().sessionClosed(this);
        if (DebugRuntimeFactory.ENABLED && DebugRuntimeFactory.getInstance().getPropertyAsBoolean("sessionHistogram")) {
            if (this.delegate instanceof SessionImplementor) {
                PersistenceContext ctx = ((SessionImplementor) this.delegate).getPersistenceContext();
                Set<Object> keySet = new HashMap<Object, Object>(ctx.getEntityEntries()).keySet();
                Map<Class, Integer> entryCount = new HashMap<Class, Integer>();
                for (Object entity : keySet) {
                    Class c = entity.getClass();
                    entryCount.put(c, this.getEntryCount(c, entryCount) + 1);
                }
                DebugRuntimeFactory.getInstance().getOrCreateMessageStore("session histogram").postMessage(entryCount.toString());
            }
        }
        return this.delegate.close();
    }

    /**
     * @param c the class
     * @param entryCount the map with entry count
     * @return int
     */
    private int getEntryCount(Class c, Map<Class, Integer> entryCount) {
        Integer old = entryCount.get(c);
        if (old == null) {
            return 0;
        }
        return old.intValue();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Connection connection() {
        return this.delegate.connection();
    }

    /**
     * {@inheritDoc}
     */
    public boolean contains(Object object) {
        return this.delegate.contains(object);
    }

    /**
     * {@inheritDoc}
     */
    public Criteria createCriteria(Class persistentClass, String alias) {
        return this.delegate.createCriteria(persistentClass, alias);
    }

    /**
     * {@inheritDoc}
     */
    public Criteria createCriteria(Class persistentClass) {
        return this.delegate.createCriteria(persistentClass);
    }

    /**
     * {@inheritDoc}
     */
    public Criteria createCriteria(String entityName, String alias) {
        return this.delegate.createCriteria(entityName, alias);
    }

    /**
     * {@inheritDoc}
     */
    public Criteria createCriteria(String entityName) {
        return this.delegate.createCriteria(entityName);
    }

    /**
     * {@inheritDoc}
     */
    public Query createFilter(Object collection, String queryString) {
        return this.delegate.createFilter(collection, queryString);
    }

    /**
     * {@inheritDoc}
     */
    public Query createQuery(String queryString) {
        return this.delegate.createQuery(queryString);
    }

    /**
     * {@inheritDoc}
     */
    public SQLQuery createSQLQuery(String queryString) {
        return this.delegate.createSQLQuery(queryString);
    }

    /**
     * {@inheritDoc}
     */
    public void delete(Object object) {
        this.markDirty(object);
        this.delegate.delete(object);
    }

    /**
     * {@inheritDoc}
     */
    public void delete(String entityName, Object object) {
        this.markDirty(object);
        this.delegate.delete(entityName, object);
    }

    /**
     * {@inheritDoc}
     */
    public void disableFilter(String filterName) {
        this.delegate.disableFilter(filterName);
    }

    /**
     * {@inheritDoc}
     */
    public Connection disconnect() {
        return this.delegate.disconnect();
    }

    /**
     * {@inheritDoc}
     */
    public void doWork(Work work) {
        this.delegate.doWork(work);
    }

    /**
     * {@inheritDoc}
     */
    public Filter enableFilter(String filterName) {
        return this.delegate.enableFilter(filterName);
    }

    /**
     * {@inheritDoc}
     */
    public void evict(Object object) {
        this.delegate.evict(object);
    }

    /**
     * {@inheritDoc}
     */
    public void flush() {
        try {
            AbstractPersister.getPersistenceTimer().start(FLUSH_IDENTIFIER);
            this.delegate.flush();
        } finally {
            AbstractPersister.getPersistenceTimer().stop(FLUSH_IDENTIFIER);
        }
    }

    /**
     * {@inheritDoc}
     */
    public Object get(Class clazz, Serializable id, LockMode lockMode) {
        return this.delegate.get(clazz, id, lockMode);
    }

    /**
     * {@inheritDoc}
     */
    public Object get(Class clazz, Serializable id) {
        return this.delegate.get(clazz, id);
    }

    /**
     * {@inheritDoc}
     */
    public Object get(String entityName, Serializable id, LockMode lockMode) {
        return this.delegate.get(entityName, id, lockMode);
    }

    /**
     * {@inheritDoc}
     */
    public Object get(String entityName, Serializable id) {
        return this.delegate.get(entityName, id);
    }

    /**
     * {@inheritDoc}
     */
    public CacheMode getCacheMode() {
        return this.delegate.getCacheMode();
    }

    /**
     * {@inheritDoc}
     */
    public LockMode getCurrentLockMode(Object object) {
        return this.delegate.getCurrentLockMode(object);
    }

    /**
     * {@inheritDoc}
     */
    public Filter getEnabledFilter(String filterName) {
        return this.delegate.getEnabledFilter(filterName);
    }

    /**
     * {@inheritDoc}
     */
    public EntityMode getEntityMode() {
        return this.delegate.getEntityMode();
    }

    /**
     * {@inheritDoc}
     */
    public String getEntityName(Object object) {
        return this.delegate.getEntityName(object);
    }

    /**
     * {@inheritDoc}
     */
    public FlushMode getFlushMode() {
        return this.delegate.getFlushMode();
    }

    /**
     * {@inheritDoc}
     */
    public Serializable getIdentifier(Object object) {
        return this.delegate.getIdentifier(object);
    }

    /**
     * {@inheritDoc}
     */
    public Query getNamedQuery(String queryName) {
        return this.delegate.getNamedQuery(queryName);
    }

    /**
     * {@inheritDoc}
     */
    public org.hibernate.Session getSession(EntityMode entityMode) {
        return this.delegate.getSession(entityMode);
    }

    /**
     * {@inheritDoc}
     */
    public SessionFactory getSessionFactory() {
        return this.delegate.getSessionFactory();
    }

    /**
     * {@inheritDoc}
     */
    public SessionStatistics getStatistics() {
        return this.delegate.getStatistics();
    }

    /**
     * {@inheritDoc}
     */
    public Transaction getTransaction() {
        return this.delegate.getTransaction();
    }

    /**
     * {@inheritDoc}
     */
    public boolean isConnected() {
        return this.delegate.isConnected();
    }

    /**
     * {@inheritDoc}
     */
    public boolean isDirty() {
        return this.delegate.isDirty();
    }

    /**
     * {@inheritDoc}
     */
    public boolean isOpen() {
        return this.delegate.isOpen();
    }

    /**
     * {@inheritDoc}
     */
    public Object load(Class theClass, Serializable id, LockMode lockMode) {
        return this.delegate.load(theClass, id, lockMode);
    }

    /**
     * {@inheritDoc}
     */
    public Object load(Class theClass, Serializable id) {
        return this.delegate.load(theClass, id);
    }

    /**
     * {@inheritDoc}
     */
    public void load(Object object, Serializable id) {
        this.delegate.load(object, id);
    }

    /**
     * {@inheritDoc}
     */
    public Object load(String entityName, Serializable id, LockMode lockMode) {
        return this.delegate.load(entityName, id, lockMode);
    }

    /**
     * {@inheritDoc}
     */
    public Object load(String entityName, Serializable id) {
        return this.delegate.load(entityName, id);
    }

    /**
     * {@inheritDoc}
     */
    public void lock(Object object, LockMode lockMode) {
        this.delegate.lock(object, lockMode);
    }

    /**
     * {@inheritDoc}
     */
    public void lock(String entityName, Object object, LockMode lockMode) {
        this.delegate.lock(entityName, object, lockMode);
    }

    /**
     * {@inheritDoc}
     */
    public Object merge(Object object) {
        Object entity = this.delegate.merge(object);
        this.markDirty(entity);
        return entity;
    }

    /**
     * {@inheritDoc}
     */
    public Object merge(String entityName, Object object) {
        Object entity = this.delegate.merge(entityName, object);
        this.markDirty(entity);
        return entity;
    }

    /**
     * {@inheritDoc}
     */
    public void persist(Object object) {
        this.markDirty(object);
        this.delegate.persist(object);
    }

    /**
     * {@inheritDoc}
     */
    public void persist(String entityName, Object object) {
        this.markDirty(object);
        this.delegate.persist(entityName, object);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void reconnect() {
        this.delegate.reconnect();
    }

    /**
     * {@inheritDoc}
     */
    public void reconnect(Connection connection) {
        this.delegate.reconnect(connection);
    }

    /**
     * {@inheritDoc}
     */
    public void refresh(Object object, LockMode lockMode) {
        this.delegate.refresh(object, lockMode);
    }

    /**
     * {@inheritDoc}
     */
    public void refresh(Object object) {
        this.delegate.refresh(object);
    }

    /**
     * {@inheritDoc}
     */
    public void replicate(Object object, ReplicationMode replicationMode) {
        this.delegate.replicate(object, replicationMode);
    }

    /**
     * {@inheritDoc}
     */
    public void replicate(String entityName, Object object, ReplicationMode replicationMode) {
        this.delegate.replicate(entityName, object, replicationMode);
    }

    /**
     * {@inheritDoc}
     */
    public Serializable save(Object object) {
        this.markDirty(object);
        return this.delegate.save(object);
    }

    /**
     * {@inheritDoc}
     */
    public Serializable save(String entityName, Object object) {
        this.markDirty(object);
        return this.delegate.save(entityName, object);
    }

    /**
     * {@inheritDoc}
     */
    public void saveOrUpdate(Object object) {
        this.markDirty(object);
        this.delegate.saveOrUpdate(object);
    }

    /**
     * {@inheritDoc}
     */
    public void saveOrUpdate(String entityName, Object object) {
        this.markDirty(object);
        this.delegate.saveOrUpdate(entityName, object);
    }

    /**
     * {@inheritDoc}
     */
    public void setCacheMode(CacheMode cacheMode) {
        this.delegate.setCacheMode(cacheMode);
    }

    /**
     * {@inheritDoc}
     */
    public void setFlushMode(FlushMode flushMode) {
        this.delegate.setFlushMode(flushMode);
    }

    /**
     * {@inheritDoc}
     */
    public void setReadOnly(Object entity, boolean readOnly) {
        this.delegate.setReadOnly(entity, readOnly);
    }

    /**
     * {@inheritDoc}
     */
    public void update(Object object) {
        this.markDirty(object);
        this.delegate.update(object);
    }

    /**
     * {@inheritDoc}
     */
    public void update(String entityName, Object object) {
        this.markDirty(object);
        this.delegate.update(entityName, object);
    }

    // Classic Methods

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Query createSQLQuery(String sql, String returnAlias, Class returnClass) {
        return this.delegate.createSQLQuery(sql, returnAlias, returnClass);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Query createSQLQuery(String sql, String[] returnAliases, Class[] returnClasses) {
        return this.delegate.createSQLQuery(sql, returnAliases, returnClasses);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public int delete(String query, Object value, Type type) {
        return this.delegate.delete(query, value, type);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public int delete(String query, Object[] values, Type[] types) {
        return this.delegate.delete(query, values, types);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public int delete(String query) {
        return this.delegate.delete(query);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Collection filter(Object collection, String filter, Object value, Type type) {
        return this.delegate.filter(collection, filter, value, type);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Collection filter(Object collection, String filter, Object[] values, Type[] types) {
        return this.delegate.filter(collection, filter, values, types);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Collection filter(Object collection, String filter) {
        return this.delegate.filter(collection, filter);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public List find(String query, Object value, Type type) {
        return this.delegate.find(query, value, type);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public List find(String query, Object[] values, Type[] types) {
        return this.delegate.find(query, values, types);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public List find(String query) {
        return this.delegate.find(query);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Iterator iterate(String query, Object value, Type type) {
        return this.delegate.iterate(query, value, type);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Iterator iterate(String query, Object[] values, Type[] types) {
        return this.delegate.iterate(query, values, types);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Iterator iterate(String query) {
        return this.delegate.iterate(query);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void save(Object object, Serializable id) {
        this.markDirty(object);
        this.delegate.save(object, id);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void save(String entityName, Object object, Serializable id) {
        this.markDirty(object);
        this.delegate.save(entityName, object, id);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object saveOrUpdateCopy(Object object, Serializable id) {
        return this.delegate.saveOrUpdateCopy(object, id);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object saveOrUpdateCopy(Object object) {
        return this.delegate.saveOrUpdateCopy(object);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object saveOrUpdateCopy(String entityName, Object object, Serializable id) {
        return this.delegate.saveOrUpdateCopy(entityName, object, id);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object saveOrUpdateCopy(String entityName, Object object) {
        return this.delegate.saveOrUpdateCopy(entityName, object);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void update(Object object, Serializable id) {
        this.markDirty(object);
        this.delegate.update(object, id);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void update(String entityName, Object object, Serializable id) {
        this.markDirty(object);
        this.delegate.update(entityName, object, id);
    }

    // Métodos de SessionImplementor

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void afterScrollOperation() {
        ((SessionImplementor) this.delegate).afterScrollOperation();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void afterTransactionCompletion(boolean successful, Transaction tx) {
        ((SessionImplementor) this.delegate).afterTransactionCompletion(successful, tx);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void beforeTransactionCompletion(Transaction tx) {
        ((SessionImplementor) this.delegate).beforeTransactionCompletion(tx);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public String bestGuessEntityName(Object object) {
        return ((SessionImplementor) this.delegate).bestGuessEntityName(object);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public int executeNativeUpdate(NativeSQLQuerySpecification specification, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).executeNativeUpdate(specification, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public int executeUpdate(String query, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).executeUpdate(query, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Batcher getBatcher() {
        return ((SessionImplementor) this.delegate).getBatcher();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Serializable getContextEntityIdentifier(Object object) {
        return ((SessionImplementor) this.delegate).getContextEntityIdentifier(object);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public int getDontFlushFromFind() {
        return ((SessionImplementor) this.delegate).getDontFlushFromFind();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Map getEnabledFilters() {
        return ((SessionImplementor) this.delegate).getEnabledFilters();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public EntityPersister getEntityPersister(String entityName, Object object) {
        return ((SessionImplementor) this.delegate).getEntityPersister(entityName, object);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object getEntityUsingInterceptor(EntityKey key) {
        return ((SessionImplementor) this.delegate).getEntityUsingInterceptor(key);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public SessionFactoryImplementor getFactory() {
        return ((SessionImplementor) this.delegate).getFactory();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public String getFetchProfile() {
        return ((SessionImplementor) this.delegate).getFetchProfile();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Type getFilterParameterType(String filterParameterName) {
        return ((SessionImplementor) this.delegate).getFilterParameterType(filterParameterName);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object getFilterParameterValue(String filterParameterName) {
        return ((SessionImplementor) this.delegate).getFilterParameterValue(filterParameterName);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Interceptor getInterceptor() {
        return ((SessionImplementor) this.delegate).getInterceptor();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public JDBCContext getJDBCContext() {
        return ((SessionImplementor) this.delegate).getJDBCContext();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public EventListeners getListeners() {
        return ((SessionImplementor) this.delegate).getListeners();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Query getNamedSQLQuery(String name) {
        return ((SessionImplementor) this.delegate).getNamedSQLQuery(name);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public PersistenceContext getPersistenceContext() {
        return ((SessionImplementor) this.delegate).getPersistenceContext();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public long getTimestamp() {
        return ((SessionImplementor) this.delegate).getTimestamp();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public String guessEntityName(Object entity) {
        return ((SessionImplementor) this.delegate).guessEntityName(entity);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object immediateLoad(String entityName, Serializable id) {
        return ((SessionImplementor) this.delegate).immediateLoad(entityName, id);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void initializeCollection(PersistentCollection collection, boolean writing) {
        ((SessionImplementor) this.delegate).initializeCollection(collection, writing);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object instantiate(String entityName, Serializable id) {
        return ((SessionImplementor) this.delegate).instantiate(entityName, id);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) {
        return ((SessionImplementor) this.delegate).internalLoad(entityName, id, eager, nullable);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public boolean isClosed() {
        return ((SessionImplementor) this.delegate).isClosed();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public boolean isEventSource() {
        return ((SessionImplementor) this.delegate).isEventSource();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public boolean isTransactionInProgress() {
        return ((SessionImplementor) this.delegate).isTransactionInProgress();
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Iterator iterate(String query, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).iterate(query, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).iterateFilter(collection, filter, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public List list(CriteriaImpl criteria) {
        return ((SessionImplementor) this.delegate).list(criteria);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public List list(NativeSQLQuerySpecification spec, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).list(spec, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public List list(String query, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).list(query, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).listCustomQuery(customQuery, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public List listFilter(Object collection, String filter, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).listFilter(collection, filter, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public ScrollableResults scroll(CriteriaImpl criteria, ScrollMode scrollMode) {
        return ((SessionImplementor) this.delegate).scroll(criteria, scrollMode);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public ScrollableResults scroll(NativeSQLQuerySpecification spec, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).scroll(spec, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public ScrollableResults scroll(String query, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).scroll(query, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) {
        return ((SessionImplementor) this.delegate).scrollCustomQuery(customQuery, queryParameters);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void setAutoClear(boolean enabled) {
        ((SessionImplementor) this.delegate).setAutoClear(enabled);
    }

    /**
     * {@inheritDoc}
     * 
     * @deprecated
     */
    @Deprecated
    public void setFetchProfile(String name) {
        ((SessionImplementor) this.delegate).setFetchProfile(name);
    }

    // Métodos de DirtyAware

    /**
     * Marca como dirty a entidade e todos os filhos com cascade caso eles implementem a interface <code>DirtyAware</code>.
     * 
     * @param entity a entidade a ser marcada.
     */
    private void markDirty(Object entity) {
        this.markDirtyRecursion(entity);
    }

    /**
     * Recursão do <code>markDirty</code>.
     * 
     * @param entity a entidade a ser marcada.
     */
    private void markDirtyRecursion(Object entity) {
        if (entity instanceof DirtyAware) {
            DirtyAware dirtyAwareEntity = (DirtyAware) entity;
            dirtyAwareEntity.setDirty(true);
        }
        for (Object element : this.getCascadeValues(entity)) {
            this.markDirtyRecursion(element);
        }
    }

    /**
     * Devolve os valores das propriedades cascade de um objeto.
     * 
     * @param object objeto.
     * @return os valores das propriedades cascade de um objeto.
     */
    private Collection getCascadeValues(Object object) {
        Collection list = new LinkedList();
        ClassMetadata classMetadata = this.getSessionFactory().getClassMetadata(this.getRealClass(object.getClass()));

        if (classMetadata == null) {
            throw new PersistenceException(PersistMessages.NOT_MAPPED.create(this.getRealClass(object.getClass())));
        }

        Type[] types = classMetadata.getPropertyTypes();
        Object[] values = classMetadata.getPropertyValues(object, EntityMode.POJO);

        for (int i = 0; i < types.length; i++) {
            Type type = types[i];
            if (!type.isAssociationType()) {
                continue;
            }

            if (!DefaultPersister.isCascadeSave(((AbstractEntityPersister) classMetadata).getCascadeStyle(i))) {
                continue;
            }

            if (!Hibernate.isInitialized(values[i])) {
                continue;
            }

            if (type.isCollectionType()) {
                if (Map.class.isAssignableFrom(type.getReturnedClass())) {
                    this.addAllNotNull((Map) values[i], list);
                } else {
                    this.addAllNotNull((Collection) values[i], list);
                }
            } else {
                if (values[i] != null) {
                    list.add(values[i]);
                }
            }
        }
        return list;
    }

    /**
     * Adiciona todos os elementos diferentes de <code>null</code> de <code>source</code> ao <code>target</code>.
     * 
     * @param source origem.
     * @param target destino.
     */
    private void addAllNotNull(Collection source, Collection target) {
        if (source == null) {
            return;
        }

        for (Object element : source) {
            if (element != null) {
                target.add(element);
            }
        }
    }

    /**
     * Adiciona todos os elementos diferentes de <code>null</code> de <code>source</code> ao <code>target</code>. Este método assume que as
     * chaves (caso sejam entidades) já estão persistidas. O mapa não poderá ser persistido caso contrário.
     * 
     * @param source origem.
     * @param target destino.
     */
    private void addAllNotNull(Map source, Collection target) {
        if (source == null) {
            return;
        }

        for (Object element : source.values()) {
            if (element != null) {
                target.add(element);
            }
        }
    }

    /**
     * Dada uma classe decorada por CGLIB devolve a classe original, se a classe não for decorada por CGLIB devolve a própria classe.
     * 
     * @param clazz classe decorada.
     * @return classe base.
     */
    private Class getRealClass(Class clazz) {
        if (clazz.getName().indexOf("$$") > 0) {
            return clazz.getSuperclass();
        } else {
            return clazz;
        }
    }
}
